package me.sdevil507.supports.shiro;

import me.sdevil507.supports.shiro.filter.JWTFilter;
import me.sdevil507.supports.shiro.filter.OrgUserFilter;
import me.sdevil507.supports.shiro.matcher.PasswordRetryHashedCredentialsMatcher;
import me.sdevil507.supports.shiro.matcher.PhoneNumberMatcher;
import me.sdevil507.supports.shiro.matcher.SmsMatcher;
import me.sdevil507.supports.shiro.matcher.WxAppletMatcher;
import me.sdevil507.supports.shiro.properties.ShiroProperties;
import me.sdevil507.supports.shiro.realm.JwtRealm;
import me.sdevil507.supports.shiro.realm.OrgSmsRealm;
import me.sdevil507.supports.shiro.realm.OrgUsernamePasswordRealm;
import me.sdevil507.supports.shiro.realm.PhoneNumberRealm;
import me.sdevil507.supports.shiro.wrap.ShiroRealmsBean;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.*;

/**
 * shiro权限框架配置
 *
 * @author sdevil507
 * @version 1.0
 */
@Configuration
@ConditionalOnProperty(name = "shiro.enable", matchIfMissing = true)
public class ShiroConfiguration {

    //region 参数
    /**
     * shiro配置参数
     */
    @Autowired
    private ShiroProperties shiroProperties;

    /**
     * 缓存管理器
     */
    @Autowired
    private CacheManager cacheManager;
    //endregion

    //region 多realm方式登录拓展入口

    /**
     * [拓展功能]多realm集合
     *
     * @return realms
     */
//    @Bean
//    public List<Realm> realms() {
//        List<Realm> realms = new ArrayList<>();
//        realms.add(orgSmsRealm());
//        realms.add(orgUsernamePasswordRealm());
//        realms.add(jwtRealm());
//        return realms;
//    }

    @Bean
    public ShiroRealmsBean realms() {
        ShiroRealmsBean shiroRealmsBean = new ShiroRealmsBean();
        shiroRealmsBean.getCollection().add(orgSmsRealm());
        shiroRealmsBean.getCollection().add(orgUsernamePasswordRealm());
        shiroRealmsBean.getCollection().add(jwtRealm());
        shiroRealmsBean.getCollection().add(phoneNumberRealm());
        return shiroRealmsBean;
    }

    /**
     * 短信登录验证域
     *
     * @return 验证域
     */
    @Bean(name = "orgSmsRealm")
    public OrgSmsRealm orgSmsRealm() {
        OrgSmsRealm orgSmsRealm = new OrgSmsRealm();
        // 注入凭证匹配器
        orgSmsRealm.setCredentialsMatcher(smsMatcher());
        // 启用缓存，默认false
        orgSmsRealm.setCachingEnabled(true);
        // 打开身份认证缓存(如果有密码变更,则需要显式调用realm.clearCachedAuthenticationInfo()方法)
        orgSmsRealm.setAuthenticationCachingEnabled(true);
        orgSmsRealm.setAuthenticationCacheName("shiro-org-sms-authenticationCache");
        // 打开授权缓存(如果有权限变更，则需要显式调用realm.clearCachedAuthorizationInfo()方法)
        orgSmsRealm.setAuthorizationCachingEnabled(true);
        orgSmsRealm.setAuthorizationCacheName("shiro-org-sms-authorizationCache");
        return orgSmsRealm;
    }

    /**
     * 短信登录匹配验证器
     *
     * @return 验证器
     */
    @Bean(name = "smsMatcher")
    public SmsMatcher smsMatcher() {
        return new SmsMatcher();
    }

    /**
     * 加密手机号登录验证域
     *
     * @return 验证域
     */
    @Bean(name = "phoneNumberRealm")
    public PhoneNumberRealm phoneNumberRealm() {
        PhoneNumberRealm phoneNumberRealm = new PhoneNumberRealm();
        // 注入凭证匹配器
        phoneNumberRealm.setCredentialsMatcher(phoneNumberMatcher());
        // 启用缓存，默认false
        phoneNumberRealm.setCachingEnabled(true);
        // 打开身份认证缓存(如果有密码变更,则需要显式调用realm.clearCachedAuthenticationInfo()方法)
        phoneNumberRealm.setAuthenticationCachingEnabled(true);
        phoneNumberRealm.setAuthenticationCacheName("shiro-iframe-authenticationCache");
        // 打开授权缓存(如果有权限变更，则需要显式调用realm.clearCachedAuthorizationInfo()方法)
        phoneNumberRealm.setAuthorizationCachingEnabled(true);
        phoneNumberRealm.setAuthorizationCacheName("shiro-iframe-authorizationCache");
        return phoneNumberRealm;
    }

    /**
     * 加密手机号登录匹配验证器
     *
     * @return 验证器
     */
    @Bean(name = "phoneNumberMatcher")
    public PhoneNumberMatcher phoneNumberMatcher() {
        return new PhoneNumberMatcher();
    }

    /**
     * 组织用户登录用户验证域
     *
     * @return 验证域
     */
    @Bean(name = "orgUsernamePasswordRealm")
    public OrgUsernamePasswordRealm orgUsernamePasswordRealm() {
        OrgUsernamePasswordRealm orgUsernamePasswordRealm = new OrgUsernamePasswordRealm();
        // 注入凭证匹配器
        orgUsernamePasswordRealm.setCredentialsMatcher(orgPasswordRetryHashedCredentialsMatcher());
        // 启用缓存，默认false
        orgUsernamePasswordRealm.setCachingEnabled(true);
        // 打开身份认证缓存并设置缓存名称
        orgUsernamePasswordRealm.setAuthenticationCachingEnabled(true);
        orgUsernamePasswordRealm.setAuthenticationCacheName("shiro-org-username-authenticationCache");
        // 打开授权缓存并设置缓存名称
        orgUsernamePasswordRealm.setAuthorizationCachingEnabled(true);
        orgUsernamePasswordRealm.setAuthorizationCacheName("shiro-org-username-authorizationCache");
        return orgUsernamePasswordRealm;
    }


    /**
     * 小程序登录匹配验证器
     *
     * @return 验证器
     */
    @Bean(name = "wxAppletMatcher")
    public WxAppletMatcher wxAppletMatcher() {
        return new WxAppletMatcher();
    }

    /**
     * 组织用户登录用户验证域
     *
     * @return 验证域
     */
    @Bean(name = "jwtRealm")
    public JwtRealm jwtRealm() {
        JwtRealm jwtRealm = new JwtRealm();
        // 注入凭证匹配器
        jwtRealm.setCredentialsMatcher(wxAppletMatcher());
        // 启用缓存，默认false
        jwtRealm.setCachingEnabled(true);
        // 打开身份认证缓存并设置缓存名称
        jwtRealm.setAuthenticationCachingEnabled(true);
        jwtRealm.setAuthenticationCacheName("shiro-jwt-username-authenticationCache");
        // 打开授权缓存并设置缓存名称
        jwtRealm.setAuthorizationCachingEnabled(true);
        jwtRealm.setAuthorizationCacheName("shiro-jwt-username-authorizationCache");
        return jwtRealm;
    }

    /**
     * 组织用户凭证匹配器
     *
     * @return RetryLimitHashedCredentialsMatcher
     */
    @Bean(name = "orgPasswordRetryHashedCredentialsMatcher")
    public PasswordRetryHashedCredentialsMatcher orgPasswordRetryHashedCredentialsMatcher() {
        PasswordRetryHashedCredentialsMatcher orgPasswordRetryHashedCredentialsMatcher = new PasswordRetryHashedCredentialsMatcher(cacheManager, "shiro-org-passwordRetryCache", shiroProperties.getPassword().getRetryCount(), shiroProperties.getPassword().getLockTime());
        orgPasswordRetryHashedCredentialsMatcher.setHashAlgorithmName("md5");
        orgPasswordRetryHashedCredentialsMatcher.setHashSalted(true);
        orgPasswordRetryHashedCredentialsMatcher.setHashIterations(1);
        orgPasswordRetryHashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return orgPasswordRetryHashedCredentialsMatcher;
    }


    //endregion

    //region [核心部分]shiro filter配置

    /**
     * shiro过滤器,用于拦截配置中的请求并进行链式处理
     *
     * @return ShiroFilterFactoryBean
     */
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, Filter> filters = new HashMap<>(20);
//        filters.put("kickout", new KickoutFilter(new MultiKickoutHandler()));
        filters.put("orgUser", new OrgUserFilter());
        filters.put("wechatUser", new JWTFilter());
//        filters.put("platUser", new PlatUserFilter());
//        filters.put("servicesUser", new ServicesUserFilter());
        shiroFilterFactoryBean.setFilters(filters);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/api/expert/user/login", "anon");
        filterChainDefinitionMap.put("/api/expert/user/resetPassword", "anon");
        filterChainDefinitionMap.put("/api/org/expert/readInvite", "anon");
        filterChainDefinitionMap.put("/api/org/expert/inviteAgree", "anon");
        filterChainDefinitionMap.put("/api/org/expert/inviteReject", "anon");
        filterChainDefinitionMap.put("/api/org/getOrgInfoByChildrenDomainName", "anon");
        filterChainDefinitionMap.put("/api/org/user/login", "anon");
        filterChainDefinitionMap.put("/api/org/user/smsLogin", "anon");
        filterChainDefinitionMap.put("/api/org/user/noCaptchaLogin", "anon");
        filterChainDefinitionMap.put("/api/org/user/resetPassword", "anon");
        filterChainDefinitionMap.put("/api/plat/util/dealExpertBiz", "anon");
        filterChainDefinitionMap.put("/api/plat/org/readAll", "anon");
        filterChainDefinitionMap.put("/api/services/user/login", "anon");
        filterChainDefinitionMap.put("/api/services/user/smsLogin", "anon");
        filterChainDefinitionMap.put("/api/services/performanceEval/exportPeFiles", "anon");
        filterChainDefinitionMap.put("/api/hgp/**", "anon");
        filterChainDefinitionMap.put("/api/common/**", "anon");
        filterChainDefinitionMap.put("/api/wechat/getUserOpenIdInfo", "anon");
        filterChainDefinitionMap.put("/api/wechat/wxManagerFastLogin", "anon");
        filterChainDefinitionMap.put("/api/wechat/compareTempExpertInfo", "anon");
        filterChainDefinitionMap.put("/api/wechat/saveWxCheckUserData", "anon");
        filterChainDefinitionMap.put("/api/wechat/getToken", "anon");
        filterChainDefinitionMap.put("/api/fruit/**", "orgUser");
        filterChainDefinitionMap.put("/api/org/**", "orgUser");
        filterChainDefinitionMap.put("/api/expert/**", "orgUser");
        filterChainDefinitionMap.put("/api/services/**", "orgUser");
        filterChainDefinitionMap.put("/api/plat/**", "orgUser");
        filterChainDefinitionMap.put("/api/wechat/**", "wechatUser");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    //endregion

}
